/*
 * Copyright (c) 1997 - 2012 Rakuten, Inc. All Rights Reserved.
 * $Id$
 * Created on 2012/05/24
 */
package com.rakuten.api.mule.model.rms;

import java.util.AbstractList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;

/**
 * 
 * @author hayatsu
 */
@JsonAutoDetect
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class MultiLang implements java.io.Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	public static final String KEY_DEFAULT_VALUE = "value";

	private String defVal;
	private Map<Locale, String> localizations;

	public MultiLang() {
	}

	public MultiLang(String defaultValue) {
		this.defVal = defaultValue;
	}

	/**
	 * Returns the default value;
	 */
	public String getValue() {
		return defVal;
	}

	/**
	 * Sets the default value.
	 */
	public void setValue(String value) {
		this.defVal = value;
	}

	/**
	 * Returns the string associated with the specified locale, or the default
	 * value if not found. If the argument is <code>null</code> or a ROOT
	 * locale, this method returns the default value({@link #getValue()}).
	 */
	public String get(Locale locale) {
		if (locale == null || localizations == null
				|| locale.equals(Locale.ROOT))
			return getValue();
		String ret = localizations.get(locale);// TODO: have to loop here
		return ret == null ? getValue() : ret;
	}

	public String getWithoutFallback(Locale locale) {
		if (locale == null || locale.equals(Locale.ROOT))
			return getValue();
		return localizations == null ? null : localizations.get(locale);
	}

	/**
	 * Sets a localized string, or, the default value if <code>locale</code> is
	 * either <code>null</code> or ROOT.
	 */
	public void set(Locale locale, String value) {
		if (locale == null || locale.equals(Locale.ROOT)) {
			setValue(value);
		} else {
			if (localizations == null)
				localizations = new java.util.HashMap<>(4);
			if (value == null)
				localizations.remove(locale);
			else
				localizations.put(locale, value);
		}
	}

	/**
	 * The locales currently stored within this object. The return value can be
	 * used for removal, but cannot be added anything.
	 */
	public Set<Locale> locales() {
		if (localizations == null)
			return Collections.emptySet();
		return localizations.keySet();
	}

	/**
	 * An iterable that first returns a ROOT locale(corresponding to the default
	 * value), and then the results of any other locales currently stored in
	 * this object.
	 */
	public Iterable<Locale> localesWithRoot() {
		return new Iterable<Locale>() {
			@Override
			public Iterator<Locale> iterator() {
				return new Iterator<Locale>() {
					Iterator<Locale> locales;

					@Override
					public boolean hasNext() {
						if (locales == null)
							return true;
						return locales.hasNext();
					}

					@Override
					public Locale next() {
						if (locales == null) {
							if (localizations == null)
								locales = Collections.emptyIterator();
							else
								locales = localizations.keySet().iterator();
							return Locale.ROOT;
						}
						return locales.next();
					}

					@Override
					public void remove() {
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}

	// TODO
	// public String bestMatch(Locale locale){
	// }

	/**
	 * Convenience method for extracting a value from a possibly
	 * <code>null</code> MultiLang instance. Useful for doing stuff like
	 * <code>MultiLang.valueOf(item.getName())</code> and not worry for null
	 * pointers.
	 */
	public static String valueOf(MultiLang lang, Locale locale) {
		return lang == null ? null : lang.get(locale);
	}

	/**
	 * Convenience method for extracting a value from a possibly
	 * <code>null</code> MultiLang instance. Useful for doing stuff like
	 * <code>MultiLang.valueOf(item.getName())</code> and not worry for null
	 * pointers.
	 */
	public static String valueOf(MultiLang lang) {
		return lang == null ? null : lang.getValue();
	}

	public static String toString(Iterable<MultiLang> langs) {
		return toString(langs, null);
	}

	public static String toString(Iterable<MultiLang> langs, Locale locale) {
		if (langs == null)
			return "null";
		StringBuilder sb = new StringBuilder().append('[');
		for (MultiLang ml : langs) {
			sb.append(valueOf(ml, locale)).append(", ");
		}
		if (sb.length() > 1)
			sb.setLength(sb.length() - 2);// drop the last delimiter with space
		return sb.append(']').toString();
	}

	public static List<String> toPlainString(final List<MultiLang> multiLangList) {
		return toPlainString(multiLangList, null);
	}

	/**
	 * Returns a read-only view of a <code>MultiLang</code> list as a plain
	 * <code>String</code> list.
	 */
	public static List<String> toPlainString(
			final List<MultiLang> multiLangList, final Locale locale) {
		if (multiLangList == null)
			return null;
		return new AbstractList<String>() {
			@Override
			public String get(int index) {
				return valueOf(multiLangList.get(index), locale);
			}

			@Override
			public int size() {
				return multiLangList.size();
			}
		};
	}
}
